from fastapi.responses import StreamingResponse
from fastapi.staticfiles import StaticFiles
from action.update import update_option, update_fft
from action.run import translate, update_run
from action.act import download, download_bom,download_model, bom_comparison
import gradio as gr
from loguru import logger
from fastapi import FastAPI, Request
import re
from public import getBOMains, save_log
from verify import itErr
import asyncio
# https://gradio.org.cn/docs/gradio/gallery


logger.add("./logs/record.log", rotation="10 MB", level='INFO', retention=3)
app = FastAPI()

# 添加中间件记录访问者 IP
@app.middleware("http")
async def log_ip(request: Request, call_next):
    try:
        rep = await call_next(request)
        if request.url.path=='/':
            body = b"".join([chunk async for chunk in rep.body_iterator])
            # 解码并替换 IP
            modified_body = re.sub(r'http://\d+\.\d+\.\d+\.\d+:\d+', '', body.decode()) \
                .replace('https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.1/iframeResizer.contentWindow.min.js',
                         f'/script/iframeResizer.contentWindow.min.js') \
                .replace('https://fonts.googleapis.com/css2','/script/css2.css')
            # 重新封装为 StreamingResponse
            new_body = modified_body.encode()
            headers = dict(rep.headers)
            headers["content-length"] = str(len(new_body))  # 更新长度
            return StreamingResponse(
                content=iter([new_body]),  # 使用迭代器模拟流式
                status_code=rep.status_code,
                headers=headers
            )
        return rep
    except Exception as e:
        logger.error(e)
        raise gr.Error(str(e))

_wait= {'fn':lambda :None, 'js':"function a(){alert('还在开发中⌛️...👀');}"}

async def _load(wl:str, request: gr.Request):
    result = await asyncio.to_thread(getBOMains, wl=wl.strip())
    if isinstance(result, str): return '⚠️'+result, None
    save_log('bom总表查询', request)
    return None, result.to_html(classes="my-table", index=False, escape=False)
    
_css = """
.my-table {
    width: 150%;
    border-collapse: collapse;
}
.my-table thead {
    /* 固定表头 */
    position: sticky;
    top: 0;
    z-index: 10;
}
.my-table th, .my-table td {
    border: 1px solid #ddd;
    padding: 8px;
    text-align: center;
}
.my-table th {
    background-color: #4CAF50;
    color: white;
}
"""

def getBlock() -> gr.Blocks:
    with gr.Blocks(title='spice在线仿真', css=_css) as demo:
        # 当做变量使用
        circuit = gr.State()
        nodes = gr.State()
        cs = gr.State()
        with gr.Tab('网表加载'):
            with gr.Row(variant='panel'):
                # 设置不同列的宽度
                with gr.Column(scale=2):
                    net = gr.Textbox(lines=25, max_lines=25, label="网表定义", interactive=True)
                with gr.Column(scale=1, variant='panel'):
                    with gr.Row():
                        seter = gr.Textbox(lines=10, max_lines=10, label="分析条件", interactive=False)
                    with gr.Row():
                        runer = gr.Textbox(lines=10, max_lines=10, label="执行命令", interactive=False)
                with gr.Column(scale=1):
                    log = gr.Textbox(lines=19, max_lines=19, label="转换日志", interactive=False)
                    # 点击下载
                    d1=gr.DownloadButton(
                        "下载脚本文件",
                        value=download,
                        inputs=[net, seter, runer]
                    )
                    d2=gr.DownloadButton(
                        "下载依赖模型文件",
                        value=download_model,
                        inputs=[net]
                    )
            with gr.Column():
                inputs=gr.Files(label='上传文件(pstchip.dat, pstxnet.dat, pstxprt.dat)',height=150)
                btfy = gr.Button("转为spice网表", variant='primary')   
        with gr.Tab('在线仿真', visible=False) as zxfz_tab:
            with gr.Row(variant='panel'):
                with gr.Column(scale=1):
                    initial = gr.Textbox(label='初始条件', placeholder='例如: V(out)=0')
                    vselect = gr.Dropdown(choices=[],label="模拟添加电压源(最多4个)", multiselect=True, max_choices=4)
                    vcontent = gr.Textbox(label="模拟电压值(直流电源直接输入电压数值)", interactive=True, placeholder='例如(交流电): SIN(0,10,1000)', visible=False)
                    analysis = gr.Dropdown(choices=['瞬态分析','交流分析','直流扫描','快速傅里叶'], label="分析模式", value='瞬态分析')
                    with gr.Column(visible=True) as tran:
                        trans=[
                            gr.Number(value=20, label="瞬时分析(ms)", minimum=0.1, interactive=True),
                            gr.Number(value=0.1, label="步长(ms)", minimum=0.1, step=0.1, interactive=True)
                        ] 
                    with gr.Column(visible=False) as ac:
                        with gr.Row():
                            acs=[
                                gr.Dropdown(choices=['oct','dec','lin'],label="分析类型", value='dec'),
                                gr.Slider(label="每倍频点数",minimum=1,value=10, maximum=2000, step=1, interactive=True),
                                gr.Textbox(label='起始频率', placeholder='例:10 10k 10Meg', interactive=True),
                                gr.Textbox(label='终止频率', placeholder='例:10 10k 10Meg', interactive=True)
                            ]
                    with gr.Column(visible=False) as dc:
                        dcs=[
                            gr.Dropdown(label="分析电源", interactive=True),
                            gr.Number(value=0, label="起始电压", minimum=0, interactive=True),
                            gr.Number(value=10, label="终止电压", minimum=0, interactive=True),
                            gr.Slider(value=0.1, label="步长(V)", minimum=0.1,maximum=100, step=0.1, interactive=True)
                        ] 
                    with gr.Column(visible=False) as four:
                        fours=[
                            gr.Number(value=20, label="瞬时分析(ms)", minimum=0.1, interactive=True),
                            gr.Number(value=0.1, label="步长(ms)", minimum=0.1, step=0.1, interactive=True),
                            gr.Dropdown(label="节点(最多4个)", multiselect=True, max_choices=4, interactive=True),
                        ] 
                    select = gr.Dropdown(choices=[],label="请添加查看信息(最多4个)", multiselect=True, max_choices=4)
                    with gr.Column():
                        btruns = [gr.Button("执行", variant='primary', visible=True),
                                gr.Button("执行", variant='primary', visible=False),
                                gr.Button("执行", variant='primary', visible=False),
                                gr.Button("执行", variant='primary', visible=False),
                                ]
                with gr.Column(scale=2):
                    runlog = gr.Textbox(lines=31, max_lines=31, label="日志", interactive=False)
                with gr.Column(scale=4):
                    img = gr.Image(label="Plot", height=680)
        with gr.Tab('AI分析'):
            with gr.Row():
                with gr.Column(scale=1):
                    bom_input=gr.File(label='上传BOM文件',height=200, file_types=['.xlsx', '.xls'])
                    # gr.DownloadButton(
                    #     "下载解密工具",
                    #     value=lambda: './static/transform.exe'
                    # )  
                    aiselect = gr.Dropdown(choices=['全部','极性电容反接校验','节点噪声分析','BOM文件校验-网表','BOM文件校验-总表'], label="分析项目", value='全部', multiselect=True, interactive=True)
                    btai = gr.Button("AI分析")
                with gr.Column(scale=5):
                    with gr.Row():
                        gr.Markdown('## 校验结果:')    
                    with gr.Row():
                        verify = gr.Markdown()
        with gr.Tab('BOM总表查询'):
            bomts = gr.Markdown()
            bomsearch = gr.Textbox(label="存货编码查询", interactive=True, placeholder="请输入存货编码, 按回车查询, 最多显示200条信息")
            bomtable = gr.HTML(max_height=680)
        with gr.Tab('BOM表对比'):
            gr.Markdown('#### 💡BOM文件等效字段: 物料编码==子件编码, 物料名称==子件名称, 物料描述==子件规格, 使用数量==基本用量  ')
            gr.Markdown('#### 以编码为主键, 如果存在多个编码相同的只会取最后一个; 编码不能为空!') 
            with gr.Row():
                with gr.Column():
                    bomdb1=gr.File(label='上传BOM1文件',height=200, file_types=['.xlsx','.xls'])
                    bomdb_result1 = gr.HTML(max_height=680)
                with gr.Column():
                    bomdb2=gr.File(label='上传BOM2文件',height=200, file_types=['.xlsx','.xls'])
                    bomdb_result2 = gr.HTML(max_height=680)
            bom_file_dn = gr.DownloadButton("下载差异表格", value=download_bom, inputs=[bomdb_result1,bomdb_result2], variant='primary', visible=False)
        option_ins = [analysis, cs, nodes, fours[2], select, dcs[0]]
        option_outs = [*btruns, tran, ac, dc, four, select, vselect, dcs[0], fours[2]]
        nodes.change(fn=update_option, inputs=option_ins, outputs=option_outs)
        analysis.input(fn=update_option, inputs=option_ins, outputs=option_outs)
        # 节点的变化设置选择也会变化, .input仅会因为用户的变化才会触发
        fours[2].input(fn=update_fft, inputs=fours[2], outputs=select)
        # 用于修改可选下拉选项, 修改下拉元素必须带select入参
        btfy.click(fn=translate, inputs=[inputs], outputs=[circuit, net, log, cs, nodes, zxfz_tab])
        for btrun, ops in zip(btruns, (trans, acs, dcs, fours)):
            btrun.click(fn=update_run,
                        inputs=[net,initial,select,vselect,vcontent,analysis,*ops], 
                        outputs=[seter,runer, runlog, img]).then(save_log, inputs=gr.State(value='在线仿真'))
        vselect.change(fn=lambda x: gr.update(visible=bool(x)), inputs=vselect, outputs=vcontent)
        btai.click(fn=itErr, inputs=[aiselect, circuit, net, initial, bom_input], outputs=[verify])
        bomsearch.submit(fn=_load, inputs=bomsearch, outputs=[bomts, bomtable])
        bomdb1.change(bom_comparison, inputs=[bomdb1, bomdb2], outputs=[bomdb_result1,bomdb_result2,bom_file_dn])
        bomdb2.change(bom_comparison, inputs=[bomdb1, bomdb2], outputs=[bomdb_result1,bomdb_result2,bom_file_dn])
    demo.queue()
    return demo

block = getBlock()
app.mount('/script', StaticFiles(directory='./static'), name='script')
# 挂载fastapi
app = gr.mount_gradio_app(app, block, path="")
